﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace iNet.IO
{
	/// <summary>
	/// 延遲建立的資料流。
	/// </summary>
	public class DelayCreationStream : Stream
	{
		#region 欄位
		Func<Stream> _CreationFunction;
		Exception _Exception;
		Stream _Stream;
		#endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 DelayCreationStream 執行個體。
		/// </summary>
		/// <param name="creationFunc">建立實際資料流的方法。</param>
		/// <exception cref="ArgumentNullException">creationFunc 為 Null (Visual Basic 為 Nothing)。</exception>
		public DelayCreationStream(Func<Stream> creationFunc)
		{
			if (creationFunc == null)
				throw new ArgumentNullException("creationFunc");
			_CreationFunction = creationFunc;
		}
		/// <summary>
		/// 初始化新的 DelayCreationStream 執行個體。
		/// </summary>
		protected DelayCreationStream()
		{ }
		#endregion
		#region CanRead
		/// <summary>
		/// 取得指示目前的資料流是否支援讀取的數值。
		/// </summary>
		public override bool CanRead
		{
			get
			{
				if (this.CreateStream(false))
					return _Stream.CanRead;
				return false;
			}
		}
		#endregion
		#region CanSeek
		/// <summary>
		/// 取得指示目前資料流是否支援搜尋的數值。
		/// </summary>
		public override bool CanSeek
		{
			get
			{
				if (this.CreateStream(false))
					return _Stream.CanSeek;
				return false;
			}
		}
		#endregion
		#region CanTimeout
		/// <summary>
		/// 取得值，該值判斷目前的資料流是否可以逾時。
		/// </summary>
		public override bool CanTimeout
		{
			get
			{
				if (this.CreateStream(false))
					return _Stream.CanTimeout;
				return false;
			}
		}
		#endregion
		#region CanWrite
		/// <summary>
		/// 取得指示目前資料流是否支援寫入的數值。
		/// </summary>
		public override bool CanWrite
		{
			get
			{
				if (this.CreateStream(false))
					return _Stream.CanWrite;
				return false;
			}
		}
		#endregion
		#region CreateInternalStream
		/// <summary>
		/// 建立實際的內部資料流，這會在第一次存取資料流時呼叫。
		/// </summary>
		/// <returns>內部資料流。</returns>
		protected virtual Stream CreateInternalStream()
		{
			return _CreationFunction();
		}
		#endregion
		#region CreateStream
		bool CreateStream(bool throwException)
		{
			lock (this)
			{
				//確認狀態
				if (_Exception != null)
				{
					if (throwException)
						throw new IOException("Fail to open internal stream", _Exception);
					return false;
				}

				//開啟資料流
				if (_Stream == null)
				{
					try
					{
						_Stream = this.CreateInternalStream();
						if (_Stream == null)
						{
							_Exception = new IOException("Fail to open internal stream");
							if (throwException)
								throw _Exception;
							return false;
						}
					}
					catch (Exception ex)
					{
						_Exception = ex;
						if (throwException)
							throw new IOException("Fail to open internal stream", ex);
						return false;
					}
				}

				//完成
				return true;
			}
		}
		#endregion
		#region Flush
		/// <summary>
		/// 清除這個資料流的所有緩衝區，並造成任何緩衝資料都寫入基礎裝置。
		/// </summary>
		public override void Flush()
		{
			this.CreateStream(true);
			_Stream.Flush();
		}
		#endregion
		#region Length
		/// <summary>
		/// 取得資料流的長度 (以位元組為單位)。
		/// </summary>
		public override long Length
		{
			get
			{
				this.CreateStream(true);
				return _Stream.Length;
			}
		}
		#endregion
		#region Position
		/// <summary>
		/// 取得或設定在目前資料流的位置。
		/// </summary>
		public override long Position
		{
			get
			{
				this.CreateStream(true);
				return _Stream.Position;
			}
			set
			{
				this.CreateStream(true);
				_Stream.Position = value;
			}
		}
		#endregion
		#region Read
		/// <summary>
		/// 自目前資料流讀取一連串的位元組，並依所讀取的位元組數目進階資料流中的位置。
		/// </summary>
		/// <param name="buffer">位元組陣列。 當這個方法返回時，緩衝區會包含具有介於 offset 和 (offset + count) 值的指定位元組陣列，由從目前來源讀取的位元組所取代。</param>
		/// <param name="offset">buffer 中以零起始的位元組位移，用來開始儲存讀取自目前資料流的資料。</param>
		/// <param name="count">自目前資料流讀取的最大位元組數。</param>
		/// <returns>緩衝區所讀取的總位元組數。 如果目前無法取得足夠的位元組，則這個數目可能小於所要求的位元組數，如果已經到達資料流末端，則為零 (0)。</returns>
		public override int Read(byte[] buffer, int offset, int count)
		{
			this.CreateStream(true);
			return _Stream.Read(buffer, offset, count);
		}
		#endregion
		#region ReadTimeout
		/// <summary>
		/// 取得或設定值 (以毫秒為單位)，該值決定在逾時前資料流將嘗試讀取多長時間。
		/// </summary>
		public override int ReadTimeout
		{
			get
			{
				this.CreateStream(true);
				return _Stream.ReadTimeout;
			}
			set
			{
				this.CreateStream(true);
				_Stream.ReadTimeout = value;
			}
		}
		#endregion
		#region Seek
		/// <summary>
		/// 設定在目前資料流的位置。
		/// </summary>
		/// <param name="offset">相對於 origin 參數的位元組位移。</param>
		/// <param name="origin">指示用以取得新位置參考點之 SeekOrigin 型別的值。</param>
		/// <returns>目前資料流的新位置。</returns>
		public override long Seek(long offset, SeekOrigin origin)
		{
			this.CreateStream(true);
			return _Stream.Seek(offset, origin);
		}
		#endregion
		#region SetLength
		/// <summary>
		/// 設定目前資料流的長度。
		/// </summary>
		/// <param name="value">目前資料流所需位元組長度。</param>
		public override void SetLength(long value)
		{
			this.CreateStream(true);
			_Stream.SetLength(value);
		}
		#endregion
		#region Write
		/// <summary>
		/// 將一連串的位元組寫入目前的資料流，並且由這個資料流中目前的位置前移寫入的位元組數目。
		/// </summary>
		/// <param name="buffer">位元組陣列。 這個方法從 buffer 複製 count 位元組到目前資料流。</param>
		/// <param name="offset">buffer 中以零起始的位元組位移，用來開始將位元組複製至目前的資料流。</param>
		/// <param name="count">寫入目前資料流的位元組數目。</param>
		public override void Write(byte[] buffer, int offset, int count)
		{
			this.CreateStream(true);
			_Stream.Write(buffer, offset, count);
		}
		#endregion
	}
}
